package org.ticlib.navigator
{
	import flash.display.DisplayObject;
	import flash.events.Event;
	import flash.events.EventDispatcher;
	
	import mx.containers.ViewStack;
	import mx.core.Container;
	import mx.events.BrowserChangeEvent;
	import mx.events.FlexEvent;
	import mx.managers.IBrowserManager;
	import mx.utils.StringUtil;
	
	[Event("change", type="flash.events.Event")]

	public class Navigator extends EventDispatcher 
	{
		private var _roleManager:IUserManager;
		private var _browserManager:IBrowserManager;
		private var _urlManager:UrlManager;
		private var _container:ViewStack;
		private var _pages:Array;
		private var _redirectionStack:Array;
		
		//--------------- Properties
	
		
		[Bindable("roleManagerChange")]
		public function get roleManager():IUserManager
		{
			return _roleManager;
		}
		
		public function set roleManager(val:IUserManager):void
		{
			_roleManager = val;
			dispatchEvent(new Event("roleManagerChange"));
		}

		[Bindable("browserManagerChange")]
		public function get browserManager():IBrowserManager
		{
			return _browserManager;
		}
		
		public function set browserManager(val:IBrowserManager):void
		{
			_browserManager = val;
			initBrowserManager();
			dispatchEvent(new Event("browserManagerChange"));
		}
		
		[Bindable("containerChange")]
		public function get container():ViewStack
		{
			return _container;
		}
		
		public function set container(value:ViewStack):void
		{
			_container = value;
			if(_container) _container.addEventListener(FlexEvent.CREATION_COMPLETE, onContainerCreationComplete);
			dispatchEvent(new Event("containerChange"));
		}
				
		public function Navigator()
		{
			_redirectionStack = new Array();
		}

		private function initBrowserManager():void
		{
			browserManager.addEventListener(BrowserChangeEvent.BROWSER_URL_CHANGE,
				onBrowserUrlChange, false, 0, true);
			browserManager.init();
			_urlManager = new UrlManager(browserManager);
		}
		
		private function onBrowserUrlChange(e:Event):void
		{
			if((_urlManager == null) || (_roleManager == null)) return;
			var rawPage:Object = _urlManager.getObjectFromUrl();
			var page:IPage = getPageByName(rawPage[UrlManager.PAGE_QUERY_STRING]);
			tryShow(page.pageName, rawPage, false, false);
		}
		
		private function onContainerCreationComplete(e:Event):void
		{
			registerChilds();
			
			var bookmark:Object = _urlManager.getObjectFromUrl();
			if(bookmark[UrlManager.PAGE_QUERY_STRING] != null)
				tryShow(bookmark[UrlManager.PAGE_QUERY_STRING], bookmark);
			else
				tryShow(getDefaultPage().pageName, null, true, false);
		}
		
		private function saveRedirection(page:String, params:Object):void
		{
			_redirectionStack.push({name:page, params:params});
		}
		
		private function getCurrentRedirection():Object
		{
			return _redirectionStack.pop();
		}
		
		private function getDefaultPage():IPage
		{
			for each(var page:Object in _pages)
			{
				if(page is IDefaultPage)
					return page as IPage;
			}
			return null;
		}
		
		private function getPageByName(name:String):IPage
		{
			for each(var page:Object in _pages)
			{
				if((page as IPage).pageName == name)
					return page as IPage;
			}
			return getDefaultPage();
		}
		
		private function getLoginPage():ILoginPage
		{
			for each(var page:Object in _pages)
			{
				if(page is ILoginPage)
					return page as ILoginPage;
			}
			return null;
		}
		
		private function isAuhorize(page:IPage):Boolean
		{
			if((page.authorizedRoles == null) || (page.authorizedRoles.length == 0)) return true;
			return _roleManager.canAccess(page.authorizedRoles);
		}
		
		//override for different type of container
		protected function navigate(page:IPage):void
		{
			container.selectedChild = page as Container;
		}
		
		//override for different type of container
		protected function registerChilds():void
		{
			_pages = new Array();
			for(var i:int = 0; i < container.numChildren; i++)
			{
				var child:DisplayObject = container.getChildAt(i);
				if(child is IPage) _pages.push(child as IPage);
				else throw new Error(StringUtil.substitute("Page at index {0} not implement IPage", i));
			}
		}
		
		private function tryShow(name:String, params:Object = null, loadInitialData:Boolean=true, writeUrl:Boolean = true):void
		{
			var page:IPage = getPageByName(name);
			//page not found show default page
			if(page == null)
			{
				var def:IPage = getDefaultPage();
				navigate(page);
				if(writeUrl) _urlManager.writeObjectToUrl(page, null);
				return;
			}
				
			if(isAuhorize(page))
			{
				//show page if user is autorized
				navigate(page);
				if(loadInitialData) page.load(params, false);
				if(writeUrl) _urlManager.writeObjectToUrl(page, params);
				return;
			}
			else
			{
				//show login page if not authorized
				saveRedirection(name, params);
				var login:IPage = getLoginPage();
				navigate(login);
				return;
			}
		}
		
		public function show(name:String, params:Object = null):void
		{
			tryShow(name, params);
		}
		
		public function pushCurrentPageToRedirectionList():void
		{
			var rawPage:Object = _urlManager.getObjectFromUrl();
			saveRedirection(rawPage[UrlManager.PAGE_QUERY_STRING], rawPage);
		}
		
		public function restoreRedirection():void
		{
			var page:Object = getCurrentRedirection();
			show(page.name, page.params);
		}
		
		public function reload():void
		{
			var rawPage:Object = _urlManager.getObjectFromUrl();
			var page:IPage = getPageByName(rawPage[UrlManager.PAGE_QUERY_STRING]);
			page.load(rawPage, true);
		}
	}
}